<?php

class Backup
{
  private $backupPath                     = null;
  private $config                         = null;
  private $jobs                           = array();
  private $logger                         = null;
  private $mode                           = Job::MODE_EXEC;
  private $notifier                       = null;
  private $pathXe                         = "";
  private $pathMysqlBin                   = "";
  
  public function __construct()
  {
    $this->logger = Logger::getRootLogger();
    $this->notifier = Notifier::getNotifier();
  }
  
  public function doJobs()
  {
    foreach($this->jobs as $job)
    {
      $this->logger->info("Start Job: ApplianceName: " . $job->getApplianceName() . " Type: " . $job->getType() . " Host: " . $job->getHost());
      
      $job->getLastBackupInformation();
      
      if(!$job->isLastBackupExpired())
      {
        continue;
      }
      
      if(!$job->isActiveTime())
      {
        continue;
      }      
      
      if($job->isDisabled())
      {
        continue;
      }
      
      $job->doBackup();
      $job->validBackup();
      
      $job->doCompress();
      
      $job->doCleanup();
    }
  }
  
  public function loadConfig($filename)
  {
    $this->logger->info("Load Config " . $filename);
    
    $this->config = new SimpleXMLElement($filename, null, true);
  
    foreach($this->config->children() as $configElement)
    {
      switch($configElement->getName())
      {
        case "BackupPath":
          $this->setBackupPath((string)$configElement);
          break;
          
        case "Mode":
          $this->setMode((string)$configElement);
          break;
          
        case "Notification":
          $this->setNotification($configElement);
          break;
          
        case "PathMysqlBin":
          $this->setPathMysqlBin((string)$configElement);
          break;          
          
        case "PathXe":
          $this->setPathXe((string)$configElement);
          break;
          
        case "Appliance":
          break;
          
        default:
          $this->logger->info("Unknown config element '" . $configElement->getName() ."'.");
          break;
      }
    }
  }
  
  private function loadJob($jobElement, $applianceName)
  {
    if($jobElement['type']!=null)
    {
      $job = null;
      try
      {
        $jobType = trim((string)$jobElement['type']);
        $className = "Job" . ucfirst($jobType);
        $job = new $className;
        if(is_a($job, $className))
        {
          $job->setName(trim((string)$jobElement['name']));
          $job->setBackupProperty("applianceName", $applianceName);
          $job->setBackupProperty("backupPath", $this->backupPath);
          $job->setBackupProperty("mode", $this->mode);
          $job->setBackupProperty("pathXe", $this->pathXe);
          $job->setBackupProperty("pathMysqlBin", $this->pathMysqlBin);
          $job->setBackupProperty("type", $jobType);
        }
        else
        {
          $subject = "Invalid Job";
          $message = "Job failed: Unknown type: '" . $jobType . "'";
          $this->logger->error($message);
          $this->notifier->send($subject, $message);
          return;
        }
      }
      catch(Exception $e)
      {
        $subject = "Invalid Job";
        $message = "Job failed: Unknown type: '" . $jobType . "'";
        $this->logger->error($message);
        $this->notifier->send($subject, $message);
        return;
      }
      
      if($job!=null)
      {
        foreach($jobElement->children() as $optionElement)
        {
          switch($optionElement->getName())
          {
            case "ActiveFrom":
              $job->setActiveFrom((string)$optionElement);
              break;
              
            case "ActiveTo":
              $job->setActiveTo((string)$optionElement);
              break;
            
            case "CompressionMode":
              $job->setCompressionMode((string)$optionElement);
              break;
            
            case "Enabled":
              $job->setEnabled((string)$optionElement);
              break;
              
            case "ExpiresAfter":
              $job->setExpiresAfter((string)$optionElement);
              break;
              
            case "BackupFilesizeMaximum":
              $job->setBackupFilesizeMaximum((string)$optionElement);
              break;
              
            case "BackupFilesizeMinimum":
              $job->setBackupFilesizeMinimum((string)$optionElement);
              break;
                          
            case "Host":
              $job->setHost((string)$optionElement);
              break;

            case "Options":
              $job->setOptions($optionElement);
              break;
              
            case "Password":
              $job->setPassword((string)$optionElement);
              break;
              
            case "Preserve":
              $job->setPreserve((string)$optionElement);
              break;

            case "User":
              $job->setUser((string)$optionElement);
              break;
                            
            default:
              $this->logger->warn("Unknown option element '" . $optionElement->getName() . "'");
              break;
          }
        }
        
        $job->postLoadJob();
        $this->jobs[] = $job;
      }
    }
    else
    {
      $subject = "Invalid Job";
      $message = "Job failed: Missing Attribute 'type' on Element Job.";
      $this->logger->error($message);
      $this->notifier->send($subject, $message);
    }
  }
  
  public function loadJobs()
  {
    foreach($this->config->children() as $configElement)
    {
      if($configElement->getName()=="Appliance")
      {
        $appName = trim((string)$configElement['name']);
        
        foreach($configElement->children() as $jobElement)
        {
          if($jobElement->getName()=="Job")
          {
            $this->loadJob($jobElement, $appName);
          }
        }
      }
    }
  }
  
  public function sendStatus()
  {
    $statusCount = array(JobStatus::ERROR=>0, JobStatus::INFO=>0, JobStatus::SUCCESSFUL=>0, JobStatus::WARNING=>0);
    
    $message = 
    "<html><body><table border='1'>" . 
    "<tr>" . 
    " <td>Jobname</td>" .
    " <td>Appliance</td>" .
    " <td>Type</td>" .
    " <td>Host</td>" .
    " <td>Status</td>" .
    " <td>Message</td>" .
    " <td>Last Backup</td> " .
    " <td>Last Backup Compression Filesize</td> " .
    "</tr>";
    
    foreach($this->jobs as $job)
    {
      $message.=
      "<tr>" .
      " <td>" . $job->getName() . "</td>" .
      " <td>" . $job->getApplianceName() . "</td>" .
      " <td>" . $job->getType() . "</td>" .
      " <td>" . $job->getHost() . "</td>" .
      " <td>";
      $status = $job->getStatus();
      $statusCount[$status->getType()]++;
      if($status->getType()==JobStatus::SUCCESSFUL)
      {
        $message.= $status->getType(); 
      }
      else if($status->getType()==JobStatus::INFO || $status->getType()==JobStatus::WARNING)
      {
        $message.= "<i>" . $status->getType() . "</i>";
      }
      else
      {
        $message.= "<b>" . $status->getType() . "</b>";
      }
      $message.=
      "</td>" .
      " <td>" . $status->getMessage() . "</td>" .
      " <td>" . $job->getLastBackupTime("d.m.Y H:i:s") . "</td>" .
      " <td>" . $job->getLastBackupCompressionFilesize() . "</td>" .
      "</tr>";
    }
    
    $message.= "</table></body></html>";
    
    if(date("H:i")=="00:00")
    {
      $subject = "Midnight Report: Successfull: " . $statusCount[JobStatus::SUCCESSFUL] . " Info: " . $statusCount[JobStatus::INFO] . " Warning: " . $statusCount[JobStatus::WARNING] . " Error: " . $statusCount[JobStatus::ERROR];
      $this->logger->info("Send midnight status.");
      $this->notifier->send($subject, $message);
    }
  }
  
  private function setBackupPath($backupPath)
  {
    $backupPath = trim($backupPath);
    
    if(substr($backupPath, -1, 1)!=DIRECTORY_SEPARATOR)
    {
      $backupPath = $backupPath . DIRECTORY_SEPARATOR;
    }
    
    $this->backupPath = $backupPath;
  }
  
  private function setNotification($notificationsElement)
  {
    foreach($notificationsElement->children() as $notificationElement)
    {
      $notification = null;
      try
      {
        $className = "Notification" . ucfirst(trim($notificationElement->getName()));
        $notification = new $className;
        if(is_a($notification, $className))
        {
          $notification->setOptions($notificationElement);
          $this->notifier->addNotification($notification);
        }
        else
        {
          $this->logger->info("Unknown notification element '" . $notificationElement->getName() ."'.");
        }
      }
      catch (Exception $e)
      {
        $this->logger->info("Unknown notification element '" . $notificationElement->getName() ."'.");
      }
    }
  }
  
  private function setMode($mode)
  {
    $mode = trim($mode);
    
    if(strtolower($mode)==Job::MODE_TEST)
    {
      $this->mode = Job::MODE_TEST;
    }
    else
    {
      $this->mode = JOB::MODE_EXEC;
    }
  }
  
  private function setPathMysqlBin($pathMysqlBin)
  {
    $pathMysqlBin = trim($pathMysqlBin);
    
    if(strlen($pathMysqlBin)>0 && substr($pathMysqlBin, -1, 1)!=DIRECTORY_SEPARATOR)
    {
      $pathMysqlBin = $pathMysqlBin . DIRECTORY_SEPARATOR;
    }
    
    $this->pathMysqlBin = $pathMysqlBin;
  }  
  
  private function setPathXe($pathXe)
  {
    $pathXe = trim($pathXe);
    
    if(strlen($pathXe)>0 && substr($pathXe, -1, 1)!=DIRECTORY_SEPARATOR)
    {
      $pathXe = $pathXe . DIRECTORY_SEPARATOR;
    }
    
    $this->pathXe = $pathXe;
  }  
  
  public function validConfig()
  {
    if(!is_dir($this->backupPath))
    {
      $subject = "Invalid Config";
      $message = "Backup failed: Backuppath '" . $this->backupPath . "' is not a directory.";
      $this->logger->error($message);
      $this->notifier->send($subject, $message);
      return false;
    }
    
    if(version_compare(PHP_VERSION, '5.2.0', '>='))
    {
      $tz = date_timezone_get(date_create());
      if($tz==false)
      {
        $subject = "Invalid Config";
        $message = "Backup failed: Timezone not set.";
        $this->logger->error($message);
        $this->notifier->send($subject, $message);
        return false;
      }
    }
    
    return true;
  }
}